home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 033a / cwexp104.zip / EXPIRE.C next >
C/C++ Source or Header  |  1991-10-22  |  23KB  |  723 lines

  1. /*
  2.     expire - A Waffle utility to provide news expiration based on age of
  3.     articles.  It can also be used to expire files in directories
  4.     related (or unrelated) to Waffle.
  5.  
  6.     Original author: Chris Winemiller, cwinemil@keys.lonestar.org
  7.  
  8.     History:
  9.         02 Apr 1991 1.0     Chris Winemiller. Original version.
  10.         09 Jul 1991 1.01    Chris Winemiller. Corrected the "usage" info
  11.                             printed out when one enters "expire -h".
  12.                             Previously, the usage explained the -t option but
  13.                             failed to place it in the invocation info. (I.e.,
  14.                             previously said "expire [-a -n]" rather than
  15.                             "expire [-a -n -t -h]".) Oh--I also added the -h
  16.                             option to this list. Actually, any option other
  17.                             than -a, -n, or -t will cause the usage to print
  18.                             out. Expanded the help output to mention the
  19.                             /mexp attribute.
  20.         20 Sep 1991 1.02    Chris Winemiller. Added "-e <expdirs_file>"
  21.                             option to name an "expdirs" file. This file will
  22.                             contain the name of directories whose files
  23.                             should be subjected to expiration.  Thanks to
  24.                             Bob Kirkpatrick (bobk@dogear.spk.wa.us) for
  25.                             suggesting the existence and format of this file.
  26.         21 Oct 1991 1.03    Modified so that expire prints out the total
  27.                             number of files and total number of bytes deleted.
  28.                             These statistics are printed just before expire
  29.                             terminates.
  30.         22 Oct 1991 1.04    Added a couple more statistics printed out at the
  31.                             end: files per second and bytes per second that
  32.                             were deleted (and total time, too).
  33.  
  34.    Invocation: expire [-a -e <expire_file> -h -n -t]
  35.         where:
  36.                -a = Consider all files in each news group directory or for
  37.                     expire directory for possible deletion. (Default:
  38.                     consider only files whose names are composed of only
  39.                     numerical characters (0-9) and no filename extension.)
  40.  
  41.                -e = Name of an "expire" file. This file contains the names
  42.                     of directories whose files should be subjected to
  43.                     expiration.  (Multiple -e options are permitted.)
  44.  
  45.                -h = Help.  Produces the "usage" printout.
  46.  
  47.                -n = No file deletions are performed, but otherwise
  48.                     produces the same output. (I.e., -n will report the
  49.                     files that should be deleted, but doesn't delete
  50.                     them.)
  51.  
  52.                -t = Display the expiration time for each news group.
  53.  
  54.                Any other attempted option produces a usage description.
  55.  
  56.    Compilation command: (Turbo C++ compiler): tcc -mt -lt expire.c getopt.c
  57.                         where -mt specifies tiny model for compilation and
  58.                         -lt tells the linker to produce a .COM executable.
  59. */
  60.  
  61. /* Page */
  62.  
  63. /*
  64.    Include files
  65.  */
  66. #include <stdio.h>
  67. #include <stdlib.h>
  68. #include <string.h>
  69. #include <dir.h>
  70. #include <dos.h>
  71. #include <time.h>
  72.  
  73. /* Page */
  74.  
  75. /*
  76.    Type definitions, etc.
  77.  */
  78.  
  79. #define VERSION "1.04  by C.L.W."
  80.  
  81. #define DEFAULT_TIME 72L /* Default expiration time of 3 days */
  82.  
  83. #define MAXPATHSIZE 128 /* Max length of any file pathname */
  84. #define MAXLINELENGTH 256 /* Max length of a line in a file */
  85.  
  86. typedef struct entry
  87. {
  88.    struct entry  *next;
  89.    char          *filename;
  90. } pathEntry;
  91.  
  92.  
  93. short prohibitDeletions = 0;  /* Turned on with -n command line option */
  94. short checkAllFiles = 0;      /* Turned on with -a command line option */
  95. short displayExpiration = 0;  /* Turned on with -t command line option */
  96.  
  97. long  totalFilesDeleted = 0L; /* Number of files deleted */
  98. long  totalBytesDeleted = 0L; /* Number of bytes deleted */
  99.  
  100. /* Page */
  101.  
  102. /*
  103.    Functions defined in other files.
  104. */   
  105.  
  106. int getopt(int nargc, char **nargv, char *ostr);
  107. extern char *optarg;
  108. extern int   optind;
  109.  
  110. /*
  111.    Functions in this file.
  112.  */
  113.  
  114. int main( int argc,  char *argv[] );
  115. pathEntry *theForumFilenames( char *staticFilename );
  116. void addToExpdirsFilenames( char *expdirsFilename, pathEntry **expdirList );
  117. void removeOldNews( char *forumFilename );
  118. void removeOldFiles( char *expdirsFilename );
  119. void getTokenValue( char *token, char *value, int *processed );
  120. void deleteOldFiles( char *directory, time_t age, pathEntry *exclusions );
  121. pathEntry *makeFilenameList( char *filenameString );
  122. void killFilenameList( pathEntry **list );
  123. void copyFilenameList( pathEntry *srclist, pathEntry **dstlist );
  124. int fileIsNotExcluded( pathEntry *list, char *name );
  125. void usage( void );
  126.  
  127. /* Page */
  128.  
  129. int main( int argc,  char *argv[] )
  130.  
  131. {
  132.     int         optionCharacter;
  133.     pathEntry   *forums = theForumFilenames( getenv( "WAFFLE" ) );
  134.     pathEntry   *expdirsList = (pathEntry *)0;
  135.     time_t      startTime = time( 0 );
  136.     time_t      elapsedTime;
  137.  
  138.     while ( ( optionCharacter = getopt( argc, argv, "ae:hnt" ) ) != EOF )
  139.     {
  140.         switch ( optionCharacter )
  141.         {
  142.             case 'a':
  143.                 checkAllFiles = 1;
  144.                 break;
  145.  
  146.             case 'n':
  147.                 prohibitDeletions = 1;
  148.                 break;
  149.  
  150.             case 't':
  151.                 displayExpiration = 1;
  152.                 break;
  153.  
  154.             case 'e':
  155.                 addToExpdirsFilenames( optarg, &expdirsList );
  156.                 break;
  157.  
  158.             case 'h':
  159.             default:
  160.                 usage();
  161.                 exit(1);
  162.                 break;
  163.         }
  164.     }
  165.                 
  166.     while ( forums )
  167.     {
  168.         removeOldNews( forums->filename );
  169.         forums = forums->next;
  170.     }
  171.  
  172.     while ( expdirsList )
  173.     {
  174.         removeOldFiles( expdirsList->filename );
  175.         expdirsList = expdirsList->next;
  176.     }
  177.  
  178.     elapsedTime = time( 0 ) - startTime;
  179.     if ( elapsedTime <= 0L ) elapsedTime = 1L;
  180.  
  181.     printf( "\n\n%ld file%s (%ld byte%s) %sdeleted at a rate of:",
  182.             totalFilesDeleted, ( totalFilesDeleted == 1L ) ? "" : "s",
  183.             totalBytesDeleted, ( totalBytesDeleted == 1L ) ? "" : "s",
  184.             prohibitDeletions ? "would have been " : "" );
  185.     printf( "\n%ld files/sec (%ld bytes/sec)",
  186.             totalFilesDeleted / elapsedTime,
  187.             totalBytesDeleted / elapsedTime );
  188.     printf( "\nElapsed time %.2ld:%.2ld:%.2ld",
  189.             elapsedTime / 3600,
  190.             elapsedTime / 60,
  191.             elapsedTime % 60 );
  192.  
  193.     printf( "\n\nExpire version %s\n", VERSION );
  194.     return 0;
  195. }
  196.  
  197. /* Page */
  198.  
  199. /*
  200.    Function:   theForumFilenames
  201.  
  202.    Purpose:    Given the name of the Waffle "Static" file, create a
  203.                linked list of filenames and return a pointer to the
  204.                first link entry. Each filename is the name of a Waffle
  205.                "Forum" type file. A forum file contains the
  206.                specifications for news groups (local or external) which
  207.                must be checked for news expiration.
  208. */
  209.  
  210. pathEntry *theForumFilenames( char *waffleStaticFilename )
  211.  
  212. {
  213.  
  214.    pathEntry        *listHead = 0;
  215.    pathEntry        *listEnd = 0;
  216.    char             *keywordSeparator;
  217.    char             waffleRootDirectory[ MAXPATHSIZE ];
  218.    char             forumFilename[ MAXPATHSIZE ];
  219.    char             line[ MAXLINELENGTH ];
  220.    char             keyword[ MAXLINELENGTH ];
  221.    FILE             *waffleFp;
  222.    int              result;
  223.  
  224.    if ( ( waffleFp = fopen( waffleStaticFilename, "r" ) ) == NULL )
  225.    {
  226.       perror( waffleStaticFilename );
  227.    }
  228.  
  229.    else
  230.    {
  231.       /* Scan the static file for forum filenames. */
  232.       while ( fgets( line, MAXLINELENGTH, waffleFp ) )
  233.       {
  234.          result = sscanf( line, " %s", keyword );
  235.          if ( result != 1 ) continue; /* No keyword on this line */
  236.  
  237.          if ( strncmpi( keyword, "waffle", 6 ) == 0 )
  238.          { /* Found keyword for waffle's root directory */
  239.             if ( ( keywordSeparator = strchr( line, ':' ) ) != 0 )
  240.             {
  241.                sscanf( ++keywordSeparator, " %s", waffleRootDirectory );
  242.             }
  243.          }
  244.          else if (strncmpi( keyword, "forums", 6 ) == 0 )
  245.          { /* Found keyword which will name forum definition file(s) */
  246.             if ( ( keywordSeparator = strchr( line, ':' ) ) != 0 )
  247.             {
  248.                ++keywordSeparator;
  249.  
  250.                do
  251.                {
  252.                   while ( (*keywordSeparator == ' ') ||
  253.                           (*keywordSeparator == '\t') ) ++keywordSeparator;
  254.  
  255.                   forumFilename[ 0 ] = '\0';
  256.                   result = sscanf( keywordSeparator, "%s", forumFilename );
  257.                   if ( result == 1 )
  258.                   {
  259.                      if ( listHead == 0 )
  260.                      {
  261.                         listHead = (pathEntry *) malloc(sizeof(pathEntry));
  262.                         listEnd = listHead;
  263.                      }
  264.                      else
  265.                      {
  266.                         listEnd->next = (pathEntry *)malloc(sizeof(pathEntry));
  267.                         if ( listEnd->next ) listEnd = listEnd->next;
  268.                      }
  269.                      listEnd->next = 0;
  270.                      listEnd->filename = malloc( MAXPATHSIZE );
  271.                      strcpy( listEnd->filename, forumFilename );
  272.                      keywordSeparator += strlen( forumFilename );
  273.  
  274.           }
  275.                }
  276.                while ( result == 1 );
  277.             }
  278.          }
  279.       }
  280.       fclose( waffleFp );
  281.    }
  282.  
  283.    for ( listEnd = listHead; listEnd; listEnd = listEnd->next )
  284.    {
  285.      strcpy ( forumFilename, listEnd->filename );
  286.      strcpy ( listEnd->filename, waffleRootDirectory );
  287.      strcat ( listEnd->filename, "/system/" );
  288.      strcat ( listEnd->filename, forumFilename );
  289.    }
  290.    return listHead;
  291. }
  292.  
  293. /* Page */
  294.  
  295. /*
  296.     Function:   addToExpdirsFilenames
  297.  
  298.     Purpose:    Add a new file name to a list of file names. This list is
  299.                 expected to be a list of "expdirs" files, each of which 
  300.                 names directories whose files should also be subjected to
  301.                 expiration.
  302. */
  303.  
  304. void addToExpdirsFilenames( char *expdirsFilename, pathEntry **expdirList )
  305. {
  306.     register pathEntry *aPath;
  307.     register pathEntry *newEntry;
  308.  
  309.     newEntry = (pathEntry *)malloc( sizeof( pathEntry ) );
  310.     newEntry->next = (pathEntry *)0;
  311.     newEntry->filename = malloc( strlen( expdirsFilename ) + 1 );
  312.     strcpy( newEntry->filename, expdirsFilename );
  313.  
  314.     if ( ( aPath = *expdirList ) != ( pathEntry *)0 )
  315.     {
  316.         while ( aPath->next ) aPath = aPath->next;
  317.         aPath->next = newEntry;
  318.     }
  319.     else *expdirList = newEntry;
  320. }
  321.  
  322. /* Page */
  323.  
  324. /*
  325.    Function:   removeOldNews
  326.  
  327.    Purpose:    Given the name of the forum file, remove news articles
  328.                according to the proper expiration time.
  329. */
  330.  
  331. void removeOldNews( char *forumFilename )
  332. {
  333.    time_t defaultExpire = DEFAULT_TIME;
  334.    time_t currentExpire = defaultExpire;
  335.    pathEntry *currentExcludes = (pathEntry *)0;
  336.    int    length;
  337.    int    is_newsgroup;
  338.    FILE  *forumFp;
  339.    char  *token;
  340.    char   parameterName[ 24 ];
  341.    char   parameterValue[ MAXPATHSIZE ];
  342.    char   line[ MAXLINELENGTH ];
  343.    char   newsGroup[ MAXLINELENGTH ];
  344.    char   newsRootDirectory[ MAXPATHSIZE ];
  345.    char   newsGroupDirectory[ MAXPATHSIZE ];
  346.  
  347.    newsGroup[ 0 ] = '\0';
  348.    newsRootDirectory[ 0 ] = '\0';
  349.    newsGroupDirectory[ 0 ] = '\0';
  350.    
  351.    if ( ( forumFp = fopen( forumFilename, "r" ) ) == 0 )
  352.    {
  353.       putchar( '\n' );
  354.       perror( forumFilename );
  355.    }
  356.    else
  357.    {
  358.       while ( fgets( line, MAXLINELENGTH, forumFp ) )
  359.       {
  360.      if ( *line == '#' ) continue;  /* Comment line; skip */
  361.          line[ strlen( line ) - 1 ] = '\0'; /* Remove '\n' at end */
  362.          newsGroupDirectory[ 0 ] = '\0';    /* Remove '\n' at end */
  363.  
  364.          token = line + strspn( line, " \t" );
  365.          if ( ( length = strcspn( token, " \t" ) ) == 0 ) continue;
  366.          strncpy( newsGroup, token, length );
  367.          newsGroup[ length ] = '\0';
  368.          is_newsgroup = ( stricmp( newsGroup, "DEFAULT" ) != 0 ) &&
  369.                         ( stricmp( newsGroup, "FORUM" ) != 0 );
  370.  
  371.          while ( token += length,
  372.                  length = strspn( token, " \t"),
  373.                  token += length,
  374.                  ( length = strcspn( token, " \t=" ) ) != 0 )
  375.          {
  376.             strncpy( parameterName, token, length );
  377.             parameterName[ length ] = '\0';
  378.             if ( token[ length ] == '=' )
  379.             {
  380.                token += length + 1;
  381.                getTokenValue( token, parameterValue, &length );
  382.             }
  383.             if ( stricmp( parameterName, "/dir" ) == 0 )
  384.             {
  385.            strcpy( is_newsgroup ? newsGroupDirectory
  386.                                     : newsRootDirectory, parameterValue );
  387.             }
  388.             else if ( stricmp( parameterName, "/mexp" ) == 0 )
  389.             {
  390.                currentExpire = atol( parameterValue );
  391.                if ( is_newsgroup == 0 ) defaultExpire = currentExpire;
  392.             }
  393.          }
  394.          if ( is_newsgroup )
  395.          {
  396.             printf( "\n%s", newsGroup );
  397.             if ( displayExpiration )
  398.             {
  399.                printf( " (%ld hour expiration)", currentExpire);
  400.             }
  401.             if ( *newsGroupDirectory == '\0' )
  402.             {
  403.                while ( ( token = strchr( newsGroup, '.' ) ) != 0 ) *token = '/';
  404.                strcpy( newsGroupDirectory, newsRootDirectory );
  405.                strcat( newsGroupDirectory, "/" );
  406.                strcat( newsGroupDirectory, newsGroup );
  407.             }
  408.             deleteOldFiles( newsGroupDirectory, currentExpire,
  409.                             currentExcludes );
  410.             currentExpire = defaultExpire;
  411.          }
  412.  
  413.       }
  414.       fclose( forumFp );
  415.    }
  416. }
  417.  
  418. /* Page */
  419.  
  420. /*
  421.     Function:   removeOldFiles
  422.  
  423.     Purpose:    Given the name of an "expdirs" file, remove files from
  424.                 the directories named therein, according to the proper
  425.                 expiration time.
  426. */
  427.  
  428. void removeOldFiles( char *expdirsFilename )
  429. {
  430.     time_t  defaultExpire = DEFAULT_TIME;
  431.     time_t  currentExpire = defaultExpire;
  432.     pathEntry *currentExcludes = (pathEntry *)0;
  433.     pathEntry *defaultExcludes = (pathEntry *)0;
  434.     int     length;
  435.     int     is_directory;
  436.     FILE   *expFp;
  437.     char   *token;
  438.     char    parameterName[ 24 ];
  439.     char    parameterValue[ MAXPATHSIZE ];
  440.     char    line[ MAXLINELENGTH ];
  441.     char    directory[ MAXLINELENGTH ];
  442.  
  443.     if ( ( expFp = fopen( expdirsFilename, "r" ) ) == 0 )
  444.     {
  445.         putchar( '\n' );
  446.         perror( expdirsFilename );
  447.     }
  448.     else
  449.     {
  450.         while ( fgets( line, MAXLINELENGTH, expFp ) )
  451.         {
  452.             if ( *line == '#' ) continue;  /* Comment line; skip */
  453.             line[ strlen( line ) - 1 ] = '\0'; /* Remove '\n' at end */
  454.             directory[ 0 ] = '\0';
  455.  
  456.             token = line + strspn( line, " \t" );
  457.             if ( ( length = strcspn( token, " \t" ) ) == 0 ) continue;
  458.             strncpy( directory, token, length );
  459.             directory[ length ] = '\0';
  460.             is_directory = ( stricmp( directory, "DEFAULT" ) != 0 );
  461.  
  462.             while ( token += length,
  463.                     length = strspn( token, " \t" ),
  464.                     token += length,
  465.                     ( length = strcspn( token, " \t=" ) ) != 0 )
  466.             {
  467.                 strncpy( parameterName, token, length );
  468.                 parameterName[ length ] = '\0';
  469.                 if ( token[ length ] == '=' )
  470.                 {
  471.                     token += length + 1;
  472.                     getTokenValue( token, parameterValue, &length );
  473.                 }
  474.                 if ( stricmp( parameterName, "/mexp" ) == 0 )
  475.                 {
  476.                     currentExpire = atol( parameterValue );
  477.                     if ( is_directory == 0 ) defaultExpire = currentExpire;
  478.                 }
  479.                 else if ( stricmp( parameterName, "/exclude" ) == 0 )
  480.                 {
  481.                     killFilenameList( ¤tExcludes );
  482.                     currentExcludes = makeFilenameList( parameterValue );
  483.                     if ( is_directory == 0 )
  484.                     {
  485.                         killFilenameList( &defaultExcludes );
  486.                         copyFilenameList( currentExcludes, &defaultExcludes );
  487.                     }
  488.             }
  489.             }
  490.             if ( is_directory )
  491.             {
  492.                 printf( "\n%s", directory );
  493.                 if ( displayExpiration )
  494.                 {
  495.                     printf( " (%ld hour expiration)", currentExpire);
  496.                 }
  497.                 deleteOldFiles( directory, currentExpire, currentExcludes );
  498.                 currentExpire = defaultExpire;
  499.                 killFilenameList( ¤tExcludes );
  500.                 copyFilenameList( defaultExcludes, ¤tExcludes );
  501.             }
  502.         }
  503.         fclose( expFp );
  504.     }
  505. }
  506.  
  507. /* Page */
  508.  
  509. /*
  510.    Function:   getTokenValue
  511.  
  512.    Purpose:    Identify the non-blank characters constituting the
  513.                value of the specified token.
  514. */
  515.  
  516. void getTokenValue( char *token, char *value, int *processed )
  517. {
  518.    int   length;
  519.    
  520.    if ( *token == '"' )
  521.    {
  522.       length = strcspn( ++token, "\"" );
  523.       strncpy( value, token, length );
  524.       value[ length ] = '\0';
  525.       length += 2;
  526.    }
  527.    else
  528.    {
  529.       length = strcspn( token, " \t" );
  530.       strncpy( value, token, length );
  531.       value[ length ] = '\0';
  532.    }
  533.    *processed = length;
  534. }
  535.    
  536. /* Page */
  537.  
  538. /*
  539.    Function:   deleteOldFiles
  540.  
  541.    Purpose:    Delete files in the specified directory which are
  542.                at least as old as the specified age.
  543. */
  544.  
  545. void deleteOldFiles( char *directory, time_t age, pathEntry *exclusions )
  546. {
  547.  
  548. #  define OUTPUTLINELENGTH 80
  549.  
  550.    struct ffblk findblock;
  551.    char         *filenameLocation;
  552.    time_t       theTime = time( 0 );
  553.    time_t       fileDate;
  554.    struct date  d_date;
  555.    struct time  d_time;
  556.    int          done;
  557.    short        fileCount;
  558.    short        currentCount;
  559.  
  560.    age *= 3600L; /* Convert age from hours to seconds */
  561.    filenameLocation = directory + strlen( directory );
  562.    strcpy( filenameLocation++, "/*.*" );
  563.  
  564.    done = findfirst( directory, &findblock, 0 );
  565.    fileCount = OUTPUTLINELENGTH / ( strlen( findblock.ff_name ) + 1 ) - 1;
  566.    currentCount = 0;
  567.  
  568.    while ( ! done )
  569.    {
  570.       d_date.da_year  = ( (findblock.ff_fdate & 0xfe00) >> 9) + 1980;
  571.       d_date.da_mon   = (findblock.ff_fdate & 0x01e0) >> 5;
  572.       d_date.da_day   = (findblock.ff_fdate & 0x1f);
  573.       d_time.ti_hour  = (findblock.ff_ftime & 0xf800) >> 11;
  574.       d_time.ti_min   = (findblock.ff_ftime & 0x07e0) >>  5;
  575.       d_time.ti_sec   = (findblock.ff_ftime & 0x1f) * 2;
  576.       d_time.ti_hund  = 0;
  577.  
  578.       fileDate = dostounix( &d_date, &d_time );
  579.  
  580.       if ( ( ( theTime - fileDate ) >= age ) &&
  581.            ( ( checkAllFiles ) ||
  582.              ( strspn( findblock.ff_name, "0123456789" ) ==
  583.                strlen(findblock.ff_name ) ) ) &&
  584.            ( fileIsNotExcluded( exclusions, findblock.ff_name ) ) )
  585.       {
  586.          if ( --currentCount <= 0 )
  587.          {
  588.             currentCount = fileCount;
  589.             putchar( '\n' );
  590.          }
  591.          strcpy( filenameLocation, findblock.ff_name );
  592.          if ( prohibitDeletions == 0 ) unlink( directory );
  593.          totalFilesDeleted++;
  594.          totalBytesDeleted += findblock.ff_fsize;
  595.          putchar( ' ' );
  596.          fputs( findblock.ff_name, stdout );
  597.       }
  598.  
  599.       done = findnext( &findblock );
  600.    }
  601. }
  602.  
  603. /* Page */
  604.  
  605. /*
  606.     Function:   makeFilenameList
  607.  
  608.     Purpose:    Make a linked list of pathEntry structures, each holding the
  609.                 name of a file. The input consists of a single character
  610.                 string containing file names separated by commas.
  611. */
  612.  
  613. pathEntry *makeFilenameList( char *filenameString )
  614. {
  615.     register pathEntry  *tmp;
  616.     int                 length = 0;
  617.     pathEntry           *theList = (pathEntry *)0;
  618.  
  619.     while ( filenameString += length,
  620.             length = strspn( filenameString, ", \t" ),
  621.             filenameString += length,
  622.             ( length = strcspn( filenameString, ", \t" ) ) != 0 )
  623.     {
  624.         tmp = (pathEntry *)malloc( sizeof( pathEntry ) );
  625.         tmp->filename = (char *)malloc( length + 1 );
  626.         strncpy( tmp->filename, filenameString, length );
  627.         tmp->filename[ length ] = '\0';
  628.         tmp->next = theList;
  629.         theList = tmp;
  630.     }
  631.  
  632.     return theList;
  633. }
  634.  
  635. /* Page */
  636.  
  637. /*
  638.     Function:   killFilenameList
  639.  
  640.     Purpose:    Delete all entries in list, and make list a null pointer.
  641. */
  642.  
  643. void killFilenameList( pathEntry **list )
  644. {
  645.     register pathEntry *tmp = *list;
  646.     register pathEntry *next;
  647.  
  648.     while ( tmp )
  649.     {
  650.         next = tmp->next;
  651.         free( (void *)tmp->filename );
  652.         free( (void *)tmp );
  653.         tmp = next;
  654.     }
  655.  
  656.     *list = (pathEntry *)0;
  657. }
  658.  
  659. /* Page */
  660.  
  661. /*
  662.     Function:   copyFilenameList
  663.  
  664.     Purpose:    create a new list (dstlist) from an existing list (srclist).
  665.                 Ignore previous contents of dstlist.
  666. */
  667.  
  668. void copyFilenameList( pathEntry *srclist, pathEntry **dstlist )
  669. {
  670.     register pathEntry  *tmp, *prevtmp;
  671.  
  672.     for ( *dstlist = prevtmp = (pathEntry *)0;
  673.           srclist;
  674.           srclist = srclist->next, prevtmp = tmp )
  675.     {
  676.         tmp = (pathEntry *)malloc( sizeof( pathEntry ) );
  677.         tmp->filename = (char *)malloc( strlen( srclist->filename ) + 1 );
  678.         strcpy( tmp->filename, srclist->filename );
  679.         tmp->next = (pathEntry *)0;
  680.         if ( prevtmp ) prevtmp->next = tmp;
  681.         else *dstlist = tmp;
  682.     }
  683. }
  684.  
  685. /* Page */
  686.  
  687. /*
  688.     Function:   fileIsNotExcluded
  689.  
  690.     Purpose:    Return true if name is not in list, false otherwise.
  691. */
  692.  
  693. int fileIsNotExcluded( register pathEntry *list, char *name )
  694. {
  695.     while ( list && ( stricmp( list->filename, name ) != 0 ) )
  696.     {
  697.         list = list->next;
  698.     }
  699.     return list == (pathEntry *)0;
  700. }
  701.  
  702. /* Page */
  703.  
  704. /*
  705.     Function:   usage
  706.  
  707.     Purpose:    Print out the usage instructions for expire.
  708. */
  709.  
  710. void usage( void )
  711. {
  712.     puts( "Usage: expire [-a -e <expire_file> -h -n -t]    where:" );
  713.     puts( "\t-a = Consider all files in each news group or expire directory." );
  714.     puts( "\t     (Default: consider only files whose names consist" );
  715.     puts( "\t     only of numerical characters (0-9) and no extension.)" );
  716.     puts( "\t-e = Name of an expire file naming directories to be expired." );
  717.     puts( "\t-h = help (i.e., this printout)");
  718.     puts( "\t-n = Reports, but doesn't delete, expired articles." );
  719.     puts( "\t-t = Also display the expiration time for each news group." );
  720.  
  721.     printf( "\nExpire version %s\n", VERSION );
  722. }
  723.